//
// Copyright (c) 2002-2009 "Neo Technology,"
//     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// This file is part of Neo4j.
// 
// Neo4j is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
// 
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
#region Using Directives

using System;
using System.Collections.Generic;
using Node = org.neo4j.graphdb.Node;
using NotFoundException = org.neo4j.graphdb.NotFoundException;

#endregion
namespace org.neo4j.kernel.impl.traversal
{
    ///
    /// <summary> A <CODE>NodeSortInfo</CODE> class represents a sort operation on a set of
    /// nodes. The client creates a <CODE>NodeSortInfo</CODE> object that can be
    /// passed to <seealso cref="java.util.Collections#sort"/> with a collection of nodes.
    /// 
    ///   The <CODE>NodeSortInfo</CODE> class is an imlementation of the typsesafe
    ///   extensible enum [see Bloch01].
    ///   
    ///   <CODE>NodeSortInfo</CODE> contains an inner class <CODE>PropertySortInfo</CODE>
    ///   that can be used by a client to sort nodes depending on a node property. Here
    ///   is a sample code snippet sorting nodes from a traverser:
    ///   
    ///   <CODE><PRE> // create the traverser ... Traverser traverser =
    ///    TraverserFactory.getFactory().createTraverser( .... ); // sort on property
    ///    "title" Traverser sortedTraverser = traverser.sort( new
    ///    NodeSortInfo.PropertySortInto( PropertyIndex.index( "title" ) ) ); // get the
    ///    nodes sorted by property "title" while ( sortedTraverser.hasNext() ) { Node
    ///    node = sortedTraverser.nextNode(); // ... }
    ///    
    ///    </PRE></CODE>
    ///   *
    /// </summary>
    /// <seealso cref= InternalTraverser </seealso>
    /// <seealso cref= java.util.Comparator </seealso>
    /// <seealso cref= java.util.Collections </seealso>
    /// 
    [Serializable]
    public abstract class NodeSortInfo<T> : IComparer<T> where T : Node
    {
        #region Implementations

        #region Public Implementations

        ///    
        ///<summary> Compares its two arguments for order. Returns a negative integer, zero,
        ///or a positive integer as the first argument is less than, equal to, or
        ///greater than the second. See <seealso cref="java.util.Comparator"/> for more
        ///information.
        ///<p>
        ///<CODE>NodeSortInfo</CODE> is intended to be used on collections of
        ///nodes. A collection containing a non <CODE>Node</CODE> element should
        ///result in a <CODE>ClassCastException</CODE>.
        /// </summary>
        ///<param name="node1">
        ///  first argument </param>
        ///<param name="node2">
        ///  second argument </param>
        ///<exception cref="ClassCastException">
        ///   if argument type is non <CODE>Node</CODE> </exception>
        ///
        public abstract int Compare(Node node1, Node node2);

        #endregion

        #region Other Implementations


        #endregion

        #endregion

        #region Internal Class


        #endregion

        /// <summary>
        /// Inner class that represents a sort operation using a node property.
        /// </summary>
        /// <typeparam name="T"></typeparam>
		public class PropertySortInfo<T> : NodeSortInfo<T> where T : Node
        {
            #region Private Variables

            /// <summary>
            /// 
            /// </summary>
            private string key = null;
            /// <summary>
            /// 
            /// </summary>
            private int direction = 1;

            #endregion

            #region Implementations

            ///<summary> 
            /// Creates a <CODE>PropertySortInfo</CODE> object using property
            ///<CODE>index</CODE> for sorting.
            /// </summary>
            ///<param name="index">
            ///      the node property used when sorting </param>
            public PropertySortInfo(string key)
            {
                this.key = key;
            }
            ///<summary>
            ///  Creates a <CODE>PropertySortInfo</CODE> object using property
            ///<CODE>index</CODE> for sorting. If <CODE>descending</CODE> is set
            ///to true the order will be reversed.
            /// </summary>
            ///<param name="index">
            ///      the node property used when sorting </param>
            ///<param name="descending">
            ///      if true order will be reversed </param>   
            public PropertySortInfo(string key, bool descending)
            {
                this.key = key;
                if (descending)
                {
                    direction = -1;
                }
            }
            ///<summary> 
            /// Compares its two arguments, see <seealso cref="Comparator#compare"/> for more
            ///information.
            ///<p>
            ///Both arguments has to be of type <CODE>Node</CODE> or a <CODE>ClassCastException</CODE>
            ///will be thrown. If the first argument lack the property used for
            ///sorting a negative integer will be returned. If the second argument
            ///lacks the property a positive integer will be returned. If both
            ///arguments lack the property zero will be returned.
            ///<p>
            ///If the properties are of the same copmarable type (such as Integer or
            ///String) the <CODE>compareTo</CODE> method on that property will be
            ///invoked passing the second property. If the properties not the same
            ///type (but have the compareTo method) they will be converted to
            ///strings (via toString method) and compared. If the first property
            ///isn't comparable but the second property is a negative integer will
            ///be returned. If both properties aren't comparable zero will be
            ///returned.
            /// </summary>
            ///<param name="node1">
            ///      the first argument </param>
            ///<param name="obj2">
            ///      the second argument </param>
            ///<exception cref="ClassCastException">
            ///       if argument type is non Node </exception>      
            public virtual int Compare<T>(Node node1, Node node2)
			{
				object property1 = null;
				object property2 = null;
				try
				{
					property1 = node1.GetProperty(key);
				}
				catch (NotFoundException e)
				{
				// ok, null then
				}
				try
				{
                    property2 = node2.GetProperty(key);
				}
				catch (NotFoundException e)
				{
				// ok, null then
				}

			// check if one or both is null
				if (property1 == null)
				{
					return (property2 == null ? 0 : -1) *direction;
				}
				if (property2 == null)
				{
					return 1 *direction;
				}

			// if property1 Integer or String, compare to property2
				if (property1 is int)
				{
					if (property2 is int)
					{
						return ((int) property1).CompareTo((int) property2) *direction;
					}
					else if (property2 is string)
					{
					// alpha numeric
						string stringValue = property1.ToString();
						return stringValue.CompareTo((string) property2) *direction;
					}
					else
					{
					// property1 wins
						return 1 *direction;
					}
				}
				if (property1 is string)
				{
					if (property2 is string)
					{
						return ((string) property1).CompareTo((string) property2) *direction;
					}
					else if (property2 is int)
					{
					// alpha numeric
						return ((string) property1).CompareTo(property2.ToString()) *direction;
					}
					else
					{
					// property1 wins
						return 1 * direction;
					}

				}
			// if property2 is Integer or String, property2 wins
				if ((property2 is int) || (property2 is string))
				{
				// property2 wins
					return -1* direction;
				}

			// we know nothing about the property types, make them equal
				return 0;
			}

            #region Overrides of NodeSortInfo<T>
   
            ///<summary> 
            /// Compares its two arguments for order. Returns a negative integer, zero,
            ///or a positive integer as the first argument is less than, equal to, or
            ///greater than the second. See Comparator for more
            ///information.
            ///<CODE>NodeSortInfo</CODE> is intended to be used on collections of
            ///nodes. A collection containing a non <CODE>Node</CODE> element should
            ///result in a <CODE>ClassCastException</CODE>.
            /// </summary>
            ///<param name="node1">
            ///  first argument </param>
            ///<param name="node2">
            ///  second argument </param>
            ///<exception cref="ClassCastException">
            ///   if argument type is non <CODE>Node</CODE> </exception>
            public override int Compare(Node node1, Node node2)
            {
                throw new NotImplementedException();
            }

            #endregion

            #endregion
		}
         #region Implementation of IComparer<T>

    /// <summary>
    /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
    /// </summary>
    /// <returns>
    /// Value 
    ///       Condition 
    ///       Less than zero
    ///   <paramref name="x"/> is less than <paramref name="y"/>.
    ///       Zero
    ///   <paramref name="x"/> equals <paramref name="y"/>.
    ///       Greater than zero
    ///   <paramref name="x"/> is greater than <paramref name="y"/>.
    /// </returns>
    /// <param name="x">The first object to compare.
    ///   </param><param name="y">The second object to compare.
    ///   </param>
    public int Compare(T x, T y)
    {
        throw new NotImplementedException();
    }

    #endregion
	}
}